package com.xbongbong.pro.dataset.node;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.xbongbong.paas.bi.pojo.DataSetNodeFieldPojo;
import com.xbongbong.paas.bi.pojo.DataSetNodeFilterPojo;
import com.xbongbong.paas.bi.pojo.DataSetNodeJoinPojo;
import com.xbongbong.paas.constant.XbbConstant;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.bi.DataSetJoinEnum;
import com.xbongbong.paas.enums.bi.DataSetNodeTypeEnum;
import com.xbongbong.paas.enums.bi.XbbTableRefEnum;
import com.xbongbong.paas.pojo.BiExplainPojo;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.bi.commons.filter.SrTeamJoinHandler;
import com.xbongbong.pro.bi.commons.helper.CollectionHelper;
import com.xbongbong.pro.bi.commons.helper.StringHelper;
import com.xbongbong.pro.bi.commons.sql.SqlBuilderHandler;
import com.xbongbong.pro.dataset.exception.ErrorCode;
import com.xbongbong.pro.dataset.exception.JsonParseException;
import com.xbongbong.pro.dataset.helper.JsonObjectHelper;
import com.xbongbong.pro.dataset.helper.JsqlParseHelper;
import com.xbongbong.pro.enums.ErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.BiErrorCodeEnum;
import com.xbongbong.pro.statistic.pojo.dto.SqlParseDTO;
import com.xbongbong.pro.statistics.help.CommonHelper;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.StringConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;


/**
 * @author gzt
 * @version 1.0
 * @Description -JOIN节点处理
 * @datetime - 2022/5/23-11:10
 * @since v1.0
 */
@Slf4j
@Component
public class JoinNodeStrategy implements NodeStrategy {
    @Resource
    JsqlParseHelper jsqlParseHelper;
    @Resource
    private SrTeamJoinHandler srTeamJoinHandler;
    @Override
    public void parseNodeToSql(SqlParseDTO sqlParseDTO) throws JsonParseException, XbbException {
        String corpid = sqlParseDTO.getCorpid();
        String nodeId = sqlParseDTO.getNodeId();
        JSONObject jsonObject = sqlParseDTO.getJsonObject();
        //DataSetNodeJoinPojo joinNode = jsonObject.toJavaObject(new TypeReference<DataSetNodeJoinPojo>() {});
        DataSetNodeJoinPojo joinNode = JSONObject.parseObject(JSON.toJSONString(jsonObject, SerializerFeature.DisableCircularReferenceDetect), DataSetNodeJoinPojo.class);
        String joinType = joinNode.getJoin();
        List<String> input = joinNode.getInput();
        int size = input.size();
        if (CollectionHelper.isEmpty(input) || size != BasicConstant.TWO){
            throw new JsonParseException(ErrorCode.MESSAGE_NOT_READABLE_ERROR);
        }
        Map<String, String> sqlNodes = SQL_NODES_THREAD_LOCAL.get();
        Map<String, String> nodeTypeThreadLocal = NODE_TYPE_THREAD_LOCAL.get();
        nodeTypeThreadLocal.put(nodeId,getNodeType());
        String nodeA = input.get(BasicConstant.ZERO);
        String nodeB = input.get(BasicConstant.ONE);
        String nodeASql = sqlNodes.get(nodeA);
        String nodeBSql = sqlNodes.get(nodeB);
        String nodeAType = nodeTypeThreadLocal.get(nodeA);//节点类型
        String nodeBType = nodeTypeThreadLocal.get(nodeB);
        if (StringUtils.isEmpty(nodeASql) || StringUtils.isEmpty(nodeBSql)){
            throw new JsonParseException(ErrorCode.SQL_ERROR);
        }
        SqlBuilderHandler sqlBuilderA = SqlBuilderHandler.start();
        SqlBuilderHandler sqlBuilderB = SqlBuilderHandler.start();
        sqlBuilderA.append(nodeASql,false);
        sqlBuilderB.append(nodeBSql,false);
        if (StringUtils.isEmpty(joinType)){
            log.error("join 连接类型为空");
            throw new XbbException(ErrorCodeEnum.API_ERROR_263005);
        }
        Map<String, List<DataSetNodeFilterPojo>> joinNodeFilter = joinNode.getFilter();
        List<Map<String, String>> joinNodeRelation = joinNode.getRelation();
        List<DataSetNodeFieldPojo> joinNodeField = joinNode.getField();
        List<DataSetNodeFieldPojo> hideField = joinNode.getHideField();
        if(CollectionsUtil.isNotEmpty(joinNodeField) && CollectionsUtil.isNotEmpty(hideField)){
            jsqlParseHelper.reMoveRedundant(joinNodeField,hideField);
            joinNodeField.addAll(hideField);
        }
        JSONArray nodeArrayA = null;
        JSONArray nodeArrayB = null;
        nodeArrayA = JsonObjectHelper.getJsonArray(joinNodeFilter.get(nodeA));
        nodeArrayB = JsonObjectHelper.getJsonArray(joinNodeFilter.get(nodeB));
        appendTeam(corpid, nodeA, nodeASql, nodeAType, sqlBuilderA, nodeArrayA);
        appendTeam(corpid, nodeB, nodeBSql, nodeBType, sqlBuilderB, nodeArrayB);

        sqlBuilderA = getSqlBuilderHandler(joinType, nodeA, nodeB, sqlBuilderA, sqlBuilderB, joinNodeRelation, joinNodeField,nodeArrayA,nodeArrayB,nodeAType,nodeBType);
        String sql = sqlBuilderA.end().getSql();
        sqlNodes.putIfAbsent(nodeId,sql);
    }

    /**
     * @author  gzt
     * @Description 关联团队表
     * @Date 17:16 2022/11/29
     * @param corpid
     * @param node
     * @param nodeSql
     * @param nodeType
     * @param sqlBuilder
     * @param nodeArray
     * @throws
     **/

    private void appendTeam(String corpid, String node,
                            String nodeSql, String nodeType, SqlBuilderHandler sqlBuilder, JSONArray nodeArray) throws XbbException {
        if (!Objects.equals(nodeType, DataSetNodeTypeEnum.JOIN.getAlias()) && !Objects.equals(nodeType, DataSetNodeTypeEnum.UNION.getAlias())) {
            Map<String,StringBuffer>  filterMap = new HashMap<>(XbbConstant.INITIALCAPACITY);
            Map<String, String> nodeTableNameMap = NODE_TABLE_NAME_THREAD_LOCAL.get();
            jsqlParseHelper.appendFilter(nodeArray, sqlBuilder, null, null, corpid, filterMap, false);
            //团队表sql获取从input节点移到合并节点
            String tableName = nodeTableNameMap.get(node);
            if (StringUtils.isNotEmpty(tableName)) {
                sqlBuilder.append(XbbConstant.L_P, XbbConstant.R_P + XbbConstant.AS + tableName, true);
            }
            XbbTableRefEnum xbbTableRefEnum = XbbTableRefEnum.getByTable(tableName);
            if (Objects.nonNull(xbbTableRefEnum)) {
                if (Objects.nonNull(filterMap)) {
                    Map<String, String> nodeFormIdMap = NODE_FORM_ID_THREAD_LOCAL.get();
                    StringBuffer condition = filterMap.get(nodeFormIdMap.get(node));
                    String teamName = CommonHelper.getTeamNameByBusinessType(xbbTableRefEnum.getCode());
                    if ((Objects.nonNull(teamName) && nodeSql.contains(teamName)) || Objects.nonNull(condition)) {
                        SqlBuilderHandler teamJoinSqlHBuilder = SqlBuilderHandler.start();
                        boolean isPublicFlag = srTeamJoinHandler.isPublicFlag(null, nodeArray, tableName, false);
                        srTeamJoinHandler.appendDataSetTeamJoinSql(teamJoinSqlHBuilder, condition, tableName, teamName, corpid, isPublicFlag);
                        String sqlB = teamJoinSqlHBuilder.end().getSql();
                        if (sqlB.length() > 0) {
                            sqlBuilder.append(sqlB);
                        }
                    }
                }
            }
        }
    }

    /**
     * @author  gzt
     * @Description join节点sql生成
     * @Date 19:42 2022/10/11
     * @param joinType
     * @param nodeA
     * @param nodeB
     * @param sqlBuilderA
     * @param sqlBuilderB
     * @param joinNodeRelation
     * @param joinNodeField
     * @return com.xbongbong.pro.bi.commons.sql.SqlBuilderHandler
     * @throws
     **/

    private SqlBuilderHandler getSqlBuilderHandler(String joinType, String nodeA, String nodeB, SqlBuilderHandler sqlBuilderA,
                                                   SqlBuilderHandler sqlBuilderB, List<Map<String, String>> joinNodeRelation,
                                                   List<DataSetNodeFieldPojo> joinNodeField,JSONArray nodeArrayA,JSONArray nodeArrayB,
                                                   String nodeAType,String nodeBType) throws XbbException {
        String sqlA = sqlBuilderA.end().getSql();
        String sqlB = sqlBuilderB.end().getSql();
        sqlBuilderA = SqlBuilderHandler.start();
        sqlBuilderB = SqlBuilderHandler.start();
        //获取join Items
        String mergeItems = joinNodeField.stream().map(field -> {
            try {
                return getMergeItemsStr(nodeA, nodeB, field,nodeAType,nodeBType);
            } catch (XbbException e) {
                return "";
            }
        }).collect(Collectors.joining(XbbConstant.SQL_COMMA));
        SqlBuilderHandler sqlBuilderA_Temp = SqlBuilderHandler.start();
        sqlBuilderB.append(StringHelper.addPreSuffix(sqlB,false)).as(XbbConstant.JOIN_R);

        if (Objects.equals(nodeAType, DataSetNodeTypeEnum.JOIN.getAlias()) || Objects.equals(nodeAType, DataSetNodeTypeEnum.UNION.getAlias())){
            sqlBuilderA_Temp.append(XbbConstant.L_P+XbbConstant.SELECT+XbbConstant.ALL_STAR).append(XbbConstant.FROM);
            sqlBuilderA_Temp.append(StringHelper.addPreSuffix(sqlA, false)+XbbConstant.AS+XbbConstant.JOIN_L);
            Map<String, StringBuffer> filterMap = new HashMap<>(XbbConstant.INITIALCAPACITY);
            jsqlParseHelper.appendFilter(nodeArrayA,sqlBuilderA_Temp,null,null,null,filterMap,true);
            sqlBuilderA_Temp.append(XbbConstant.R_P+XbbConstant.AS+XbbConstant.JOIN_L);
        }else {
            sqlBuilderA_Temp.append(StringHelper.addPreSuffix(sqlA, false)).as(XbbConstant.JOIN_L);
        }

        if (Objects.equals(nodeBType, DataSetNodeTypeEnum.JOIN.getAlias()) || Objects.equals(nodeBType, DataSetNodeTypeEnum.UNION.getAlias())){
            SqlBuilderHandler sqlBuilderTemp = SqlBuilderHandler.start();
            sqlBuilderTemp.append(XbbConstant.L_P+XbbConstant.SELECT+XbbConstant.ALL_STAR).append(XbbConstant.FROM);
            sqlBuilderTemp.append(sqlBuilderB.end().getSql());
            sqlBuilderB = sqlBuilderTemp;
            Map<String, StringBuffer> filterMap = new HashMap<>(XbbConstant.INITIALCAPACITY);
            jsqlParseHelper.appendFilter(nodeArrayB,sqlBuilderB,null,null,null,filterMap,true);
            sqlBuilderB.append(XbbConstant.R_P+XbbConstant.AS+XbbConstant.JOIN_R);
        }

        sqlBuilderA.select(mergeItems).from(sqlBuilderA_Temp.end().getSql());
        String joinBSql = sqlBuilderB.end().getSql();
        DataSetJoinEnum joinEnum = DataSetJoinEnum.getByAlias(joinType);
        if (Objects.isNull(joinEnum)) {
            throw new XbbException(ErrorCodeEnum.API_ERROR_263007);
        }
        switch (joinEnum) {
            //左连接用左表、右连接用右表，内连接统一用左表
            case LEFT:
                sqlBuilderA.leftJoin(joinBSql);
                break;
            case INNER:
                sqlBuilderA.innerJoin(joinBSql);
                break;
            case RIGHT:
                sqlBuilderA.rightJoin(joinBSql);
                break;
            default:
                break;
        }
        int rSize = joinNodeRelation.size();
        for (int i = 0; i < rSize; i++) {
            Map<String, String> stringObjectHashMap = joinNodeRelation.get(i);
            String onLeft = StringHelper.getString(stringObjectHashMap.get(nodeA));
            String onRight = StringHelper.getString(stringObjectHashMap.get(nodeB));
            if (StringUtils.isNotEmpty(onLeft) && StringUtils.isNotEmpty(onRight)
                    && StringUtils.containsAny(onLeft,XbbConstant.AMPERSAND) && StringUtils.containsAny(onRight,XbbConstant.AMPERSAND) ) {
                String columnL = onLeft.split(XbbConstant.AMPERSAND)[1];
                String columnR = onRight.split(XbbConstant.AMPERSAND)[1];
                String columnFieldTypeL = onLeft.split(XbbConstant.AMPERSAND)[2];
                String columnFieldTypeR = onRight.split(XbbConstant.AMPERSAND)[2];
                if (StringUtils.isNotEmpty(columnFieldTypeL) && StringUtils.isNotEmpty(columnFieldTypeR)) {
                    Integer filedTypeL = Integer.parseInt(columnFieldTypeL);
                    Integer filedTypeR = Integer.parseInt(columnFieldTypeR);
                    columnL = convertUserAttr(columnL, filedTypeL,nodeAType);
                    columnR = convertUserAttr(columnR, filedTypeR,nodeBType);
                }
                if (i == 0) {
                    sqlBuilderA.on(MessageFormat.format("{0}{1}{2}", getJoinAttr(XbbConstant.JOIN_L, columnL), XbbConstant.EQUAL, getJoinAttr(XbbConstant.JOIN_R, columnR)));
                } else {
                    sqlBuilderA.and(MessageFormat.format("{0}{1}{2}{3}{4}{5}{6}", XbbConstant.JOIN_L, XbbConstant.POINT, columnL, XbbConstant.EQUAL, XbbConstant.JOIN_R, XbbConstant.POINT, columnR));
                }
            }
        }
        return sqlBuilderA;
    }

    /**
     * @author  gzt
     * @Description 负责人协同人字段属性转换
     * @Date 10:49 2022/10/18
     * @param column
     * @param filedTypeR
     * @return java.lang.String
     * @throws
     **/

    private String convertUserAttr(String column, Integer filedTypeR,String nodeType) {
        if (Objects.equals(FieldTypeEnum.OWNERID.getType(), filedTypeR)
                || Objects.equals(FieldTypeEnum.COUSERID.getType(), filedTypeR)) {
            column = CommonHelper.getConvertAttr(column, filedTypeR);
        } else {
            //解决多层和单层join data外字段驼峰转下划线。多层的不需要转
            if (!Objects.equals(nodeType, DataSetNodeTypeEnum.JOIN.getAlias()) && !Objects.equals(nodeType,DataSetNodeTypeEnum.UNION.getAlias())){
                column = StringUtil.getDataBaseString(column);
            }
        }
        return column;
    }

    /**
     * @author  gzt
     * @Description 根据左右节点、Joinziduan获取merge Items
     * @Date 19:28 2022/10/8
     * @param nodeA
     * @param nodeB
     * @param field
     * @return java.lang.String
     * @throws XbbException
     **/
    private String getMergeItemsStr(String nodeA, String nodeB, DataSetNodeFieldPojo field,String nodeAType,String nodeBType) throws XbbException {
        Integer fieldType = field.getFieldType();
        Map<String, BiExplainPojo> srcB = field.getSrcB();
        if (Objects.isNull(srcB)){
            log.error("join field.getSrcB对象为空");
            throw new XbbException(BiErrorCodeEnum.API_ERROR_241043);
        }
        BiExplainPojo biExplainPojo = srcB.get(nodeA);
        String pre = XbbConstant.JOIN_L;
        if (Objects.isNull(biExplainPojo)) {
            biExplainPojo = srcB.get(nodeB);
            pre = XbbConstant.JOIN_R;
        }
        if (Objects.isNull(biExplainPojo)){
            log.error("join biExplainPojo连接为空");
            throw new XbbException(BiErrorCodeEnum.API_ERROR_241044);
        }
        String attr = biExplainPojo.getAttr();
        String originalAttr = biExplainPojo.getOriginalAttr();
        if (StringUtils.isNotEmpty(originalAttr) && StringUtils.contains(originalAttr,FieldTypeEnum.SUB_FORM.getEsalias())){
            originalAttr = originalAttr.split("\\"+XbbConstant.POINT)[0];
        }else {
            //解决多层和单层join data外字段驼峰转下划线。多层的不需要转
            if(StringUtils.equals(XbbConstant.JOIN_L,pre)){
                if (!Objects.equals(nodeAType, DataSetNodeTypeEnum.JOIN.getAlias()) && !Objects.equals(nodeAType,DataSetNodeTypeEnum.UNION.getAlias())){
                    originalAttr = StringUtil.getDataBaseString(originalAttr);
                }else {
                    if(originalAttr.contains(FieldTypeEnum.FORM_ID.getAlias())){
                        originalAttr  = StringUtil.getDataBaseString(originalAttr);
                    }
                }
            }else if (StringUtils.equals(XbbConstant.JOIN_R,pre)){
                if (!Objects.equals(nodeBType, DataSetNodeTypeEnum.JOIN.getAlias()) && !Objects.equals(nodeBType,DataSetNodeTypeEnum.UNION.getAlias())){
                    originalAttr = StringUtil.getDataBaseString(originalAttr);
                }else {
                    if(originalAttr.contains(FieldTypeEnum.FORM_ID.getAlias())){
                        originalAttr  = StringUtil.getDataBaseString(originalAttr);
                    }
                }
            }
            if (Objects.equals(FieldTypeEnum.OWNERID.getType(),fieldType)) {
                //处理没有团队表的情况
                originalAttr = XbbConstant.MY_TEAM_MAIN + StringConstant.POSITIVE_NUMBER;
                if (attr.contains(XbbConstant.SEPARATOR)){
                    attr= StringHelper.replaceTeamAttr(attr, XbbConstant.MY_TEAM_MAIN);
                }else {
                    attr = originalAttr;
                }
            } else if (Objects.equals(FieldTypeEnum.COUSERID.getType(),fieldType)) {
                //处理没有团队表的情况
                originalAttr = XbbConstant.MY_TEAM + StringConstant.POSITIVE_NUMBER;
                if (attr.contains(XbbConstant.SEPARATOR)){
                    attr= StringHelper.replaceTeamAttr(attr, XbbConstant.MY_TEAM);
                }else {
                    attr = originalAttr;
                }
            }else {
                if(attr.contains(FieldTypeEnum.FORM_ID.getAlias())){
                    attr = StringUtil.getDataBaseString(attr);
                }
            }
        }
        return String.format("%s%s%s%s%s", pre, XbbConstant.POINT, originalAttr, XbbConstant.AS, attr);
    }


    private String getJoinAttr(String table ,String linkAttrA) {
        /*if(linkAttrA.contains("num") || linkAttrA.contains("date")){
            return table+XbbConstant.POINT+linkAttrA;
        }
        if (!(Objects.equals("corpid", linkAttrB) || Objects.equals("serial_no", linkAttrB) || Objects.equals("owner_id", linkAttrB) || Objects.equals("creator_id", linkAttrB))) {
            //toInt64OrZero(JSONExtractString(tb_saas_contract.data, 'text_2'))
            StringBuffer sqlBuffer = new StringBuffer();
            if (linkAttrB.contains("id") || linkAttrB.contains("date") || linkAttrB.contains("Time")) {
                //sqlBuffer.append(XbbConstant.TO_INT_64_OR_ZERO).append("(").append(XbbConstant.JSON_EXTRACT_STRING);
                //sqlBuffer.append(XbbConstant.TO_INT_64_OR_ZERO).append(XbbConstant.L_P).append(table+"."+linkAttrA).append(XbbConstant.R_P);
                sqlBuffer.append(table+XbbConstant.POINT+linkAttrA);
                    /*if (Objects.nonNull(tableAlias) && (StringUtils.contains(tableAlias, XbbTableRefEnum.PAY_SHEET.getTableName()) ||
                            StringUtils.contains(tableAlias,XbbTableRefEnum.PAYMENT_SHEET.getTableName()))){
                        sqlBuffer.append("replaceRegexpAll("+CommonHelper.getExtractString(funName,tableAlias,linkAttrA)+"," +
                                "  '(\\\\[)(.*)(\\\\])','\\\\2')").append(XbbConstant.R_P);
                    }else {
                    }

                return  sqlBuffer.toString();
            }else {
                return table+XbbConstant.POINT+linkAttrA;
            }
        }else {
            return table+XbbConstant.POINT+linkAttrA;
        }*/
        return table+XbbConstant.POINT+linkAttrA;
    }

    @Override
    public String getNodeType() {
        return DataSetNodeTypeEnum.JOIN.getAlias();
    }

    @Override
    public String getParseSql(String nodeId) {
        return SQL_NODES_THREAD_LOCAL.get().get(nodeId);
    }
}
